<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
    <meta
      name="description"
      content="User-defined clipping planes applied to a batched 3D Tileset, point cloud, and model."
    />
    <meta name="cesium-sandcastle-labels" content="Showcases, 3D Tiles" />
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script
      type="text/javascript"
      src="../../../Build/CesiumUnminified/Cesium.js"
      nomodule
    ></script>
    <script type="module" src="../load-cesium-es6.js"></script>
  </head>
  <body
    class="sandcastle-loading"
    data-sandcastle-bucket="bucket-requirejs.html"
  >
    <style>
      @import url(../templates/bucket.css);
      #toolbar {
        background: rgba(42, 42, 42, 0.8);
        padding: 4px;
        border-radius: 4px;
      }
      #toolbar input {
        vertical-align: middle;
        padding-top: 2px;
        padding-bottom: 2px;
      }
      #toolbar .header {
        font-weight: bold;
      }
    </style>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="loadingOverlay"><h1>Loading...</h1></div>
    <div id="toolbar">
      <select
        data-bind="options: exampleTypes, value: currentExampleType"
      ></select>
      <input
        type="checkbox"
        value="false"
        data-bind="checked: debugBoundingVolumesEnabled, valueUpdate: 'input'"
      />
      Show bounding volume
      <input
        type="checkbox"
        value="true"
        data-bind="checked: edgeStylingEnabled, valueUpdate: 'input'"
      />
      Enable edge styling
    </div>

    <script id="cesium_sandcastle_script">
      window.startup = async function (Cesium) {
        "use strict";
        //Sandcastle_Begin
        // Add a clipping plane, a plane geometry to show the representation of the
        // plane, and control the magnitude of the plane distance with the mouse.

        const viewer = new Cesium.Viewer("cesiumContainer", {
          infoBox: false,
          selectionIndicator: false,
        });
        const scene = viewer.scene;

        viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
          "2022-08-01T00:00:00Z"
        );

        const clipObjects = ["BIM", "Point Cloud", "Instanced", "Model"];
        const viewModel = {
          debugBoundingVolumesEnabled: false,
          edgeStylingEnabled: true,
          exampleTypes: clipObjects,
          currentExampleType: clipObjects[0],
        };

        let targetY = 0.0;
        let planeEntities = [];
        let selectedPlane;
        let clippingPlanes;

        // Select plane when mouse down
        const downHandler = new Cesium.ScreenSpaceEventHandler(
          viewer.scene.canvas
        );
        downHandler.setInputAction(function (movement) {
          const pickedObject = scene.pick(movement.position);
          if (
            Cesium.defined(pickedObject) &&
            Cesium.defined(pickedObject.id) &&
            Cesium.defined(pickedObject.id.plane)
          ) {
            selectedPlane = pickedObject.id.plane;
            selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
            selectedPlane.outlineColor = Cesium.Color.WHITE;
            scene.screenSpaceCameraController.enableInputs = false;
          }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        // Release plane on mouse up
        const upHandler = new Cesium.ScreenSpaceEventHandler(
          viewer.scene.canvas
        );
        upHandler.setInputAction(function () {
          if (Cesium.defined(selectedPlane)) {
            selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.1);
            selectedPlane.outlineColor = Cesium.Color.WHITE;
            selectedPlane = undefined;
          }

          scene.screenSpaceCameraController.enableInputs = true;
        }, Cesium.ScreenSpaceEventType.LEFT_UP);

        // Update plane on mouse move
        const moveHandler = new Cesium.ScreenSpaceEventHandler(
          viewer.scene.canvas
        );
        moveHandler.setInputAction(function (movement) {
          if (Cesium.defined(selectedPlane)) {
            const deltaY = movement.startPosition.y - movement.endPosition.y;
            targetY += deltaY;
          }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

        function createPlaneUpdateFunction(plane) {
          return function () {
            plane.distance = targetY;
            return plane;
          };
        }

        let tileset;
        async function loadTileset(resource, modelMatrix) {
          const currentExampleType = viewModel.currentExampleType;

          clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: [
              new Cesium.ClippingPlane(
                new Cesium.Cartesian3(0.0, 0.0, -1.0),
                0.0
              ),
            ],
            edgeWidth: viewModel.edgeStylingEnabled ? 1.0 : 0.0,
          });

          try {
            const url = await Promise.resolve(resource);
            tileset = await Cesium.Cesium3DTileset.fromUrl(url, {
              clippingPlanes: clippingPlanes,
            });

            if (currentExampleType !== viewModel.currentExampleType) {
              // Another tileset was loaded, discard the current result
              return;
            }

            if (Cesium.defined(modelMatrix)) {
              tileset.modelMatrix = modelMatrix;
            }

            viewer.scene.primitives.add(tileset);

            tileset.debugShowBoundingVolume =
              viewModel.debugBoundingVolumesEnabled;
            const boundingSphere = tileset.boundingSphere;
            const radius = boundingSphere.radius;

            viewer.zoomTo(
              tileset,
              new Cesium.HeadingPitchRange(0.5, -0.2, radius * 4.0)
            );

            if (
              !Cesium.Matrix4.equals(
                tileset.root.transform,
                Cesium.Matrix4.IDENTITY
              )
            ) {
              // The clipping plane is initially positioned at the tileset's root transform.
              // Apply an additional matrix to center the clipping plane on the bounding sphere center.
              const transformCenter = Cesium.Matrix4.getTranslation(
                tileset.root.transform,
                new Cesium.Cartesian3()
              );
              const transformCartographic = Cesium.Cartographic.fromCartesian(
                transformCenter
              );
              const boundingSphereCartographic = Cesium.Cartographic.fromCartesian(
                tileset.boundingSphere.center
              );
              const height =
                boundingSphereCartographic.height -
                transformCartographic.height;
              clippingPlanes.modelMatrix = Cesium.Matrix4.fromTranslation(
                new Cesium.Cartesian3(0.0, 0.0, height)
              );
            }

            for (let i = 0; i < clippingPlanes.length; ++i) {
              const plane = clippingPlanes.get(i);
              const planeEntity = viewer.entities.add({
                position: boundingSphere.center,
                plane: {
                  dimensions: new Cesium.Cartesian2(radius * 2.5, radius * 2.5),
                  material: Cesium.Color.WHITE.withAlpha(0.1),
                  plane: new Cesium.CallbackProperty(
                    createPlaneUpdateFunction(plane),
                    false
                  ),
                  outline: true,
                  outlineColor: Cesium.Color.WHITE,
                },
              });

              planeEntities.push(planeEntity);
            }
            return tileset;
          } catch (error) {
            console.log(`Error loading  tileset: ${error}`);
          }
        }

        function loadModel(url) {
          clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: [
              new Cesium.ClippingPlane(
                new Cesium.Cartesian3(0.0, 0.0, -1.0),
                0.0
              ),
            ],
            edgeWidth: viewModel.edgeStylingEnabled ? 1.0 : 0.0,
          });

          const position = Cesium.Cartesian3.fromDegrees(
            -123.0744619,
            44.0503706,
            300.0
          );
          const heading = Cesium.Math.toRadians(135.0);
          const pitch = 0.0;
          const roll = 0.0;
          const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
          const orientation = Cesium.Transforms.headingPitchRollQuaternion(
            position,
            hpr
          );
          const entity = viewer.entities.add({
            name: url,
            position: position,
            orientation: orientation,
            model: {
              uri: url,
              scale: 8,
              minimumPixelSize: 100.0,
              clippingPlanes: clippingPlanes,
            },
          });

          viewer.trackedEntity = entity;

          for (let i = 0; i < clippingPlanes.length; ++i) {
            const plane = clippingPlanes.get(i);
            const planeEntity = viewer.entities.add({
              position: position,
              plane: {
                dimensions: new Cesium.Cartesian2(300.0, 300.0),
                material: Cesium.Color.WHITE.withAlpha(0.1),
                plane: new Cesium.CallbackProperty(
                  createPlaneUpdateFunction(plane),
                  false
                ),
                outline: true,
                outlineColor: Cesium.Color.WHITE,
              },
            });

            planeEntities.push(planeEntity);
          }
        }

        // Power Plant design model provided by Bentley Systems
        const bimUrl = Cesium.IonResource.fromAssetId(2464651);
        const pointCloudUrl = Cesium.IonResource.fromAssetId(16421);
        const instancedUrl =
          "../../SampleData/Cesium3DTiles/Instanced/InstancedOrientation/tileset.json";
        const modelUrl = "../../SampleData/models/CesiumAir/Cesium_Air.glb";

        loadTileset(bimUrl);

        // Track and create the bindings for the view model
        const toolbar = document.getElementById("toolbar");
        Cesium.knockout.track(viewModel);
        Cesium.knockout.applyBindings(viewModel, toolbar);

        Cesium.knockout
          .getObservable(viewModel, "currentExampleType")
          .subscribe(function (newValue) {
            reset();

            if (newValue === clipObjects[0]) {
              loadTileset(bimUrl);
            } else if (newValue === clipObjects[1]) {
              loadTileset(pointCloudUrl);
            } else if (newValue === clipObjects[2]) {
              // Position the instanced tileset above terrain
              loadTileset(
                instancedUrl,
                Cesium.Matrix4.fromTranslation(
                  new Cesium.Cartesian3(15.0, -58.6, 50.825)
                )
              );
            } else {
              loadModel(modelUrl);
            }
          });

        Cesium.knockout
          .getObservable(viewModel, "debugBoundingVolumesEnabled")
          .subscribe(function (value) {
            if (Cesium.defined(tileset)) {
              tileset.debugShowBoundingVolume = value;
            }
          });

        Cesium.knockout
          .getObservable(viewModel, "edgeStylingEnabled")
          .subscribe(function (value) {
            const edgeWidth = value ? 1.0 : 0.0;

            clippingPlanes.edgeWidth = edgeWidth;
          });

        function reset() {
          viewer.entities.removeAll();
          if (Cesium.defined(tileset)) {
            viewer.scene.primitives.remove(tileset);
          }

          planeEntities = [];
          targetY = 0.0;
          tileset = undefined;
        }

        //Sandcastle_End
      };
      if (typeof Cesium !== "undefined") {
        window.startupCalled = true;
        window.startup(Cesium).catch((error) => {
          "use strict";
          console.error(error);
        });
        Sandcastle.finishedLoading();
      }
    </script>
  </body>
</html>
